# however, we must simplify the code by assuming some unrealistic conditions.
import logging
from abc import ABC, abstractmethod
from typing import Callable, Dict, Iterable, List, Optional, Self, Set, Type

from tree_sitter import Node

from chernc.analyzer.utils import (
    MatchData,
    NoSuchChild,
    NoText,
    RustNode,
    match_exact_str,
    match_exactly,
    match_exactly_lower,
    match_similar_str,
    match_similarly,
    match_similarly_lower,
    merge_matches,
    unsafe_child_by_name,
    unsafe_child_text,
    unsafe_declarator_name,
    unsafe_text,
)
from chernc.constants import DEFAULT_LOGGER_DIR, DEFAULT_LOGGER_FILE_NAME
from chernc.logging import build_logger
from chernc.parser.cpp_parser import parse_cpp
from chernc.parser.tree_sitter_utils import has_named_child
from chernc.slicer.code_slice import CodeSlice

# a semantic piece is the least unit of matching
# while a code slice and a code chunk(see slicer) are *syntactical units*
# a semantic piece is a *semantic unit*, which means they are semantically
# related, and LLM *MAY* treat them as own.

# The mission of this class is closely related to the slice-based translation process
# A SemanticPiece usually in the same file


logger: logging.Logger = build_logger(
    __name__ + ".semanticpiece", logger_filename=DEFAULT_LOGGER_FILE_NAME, logger_dir=DEFAULT_LOGGER_DIR
)


CONCRETE_TYPES = []


def conrete(cls):
    global CONCRETE_TYPES
    CONCRETE_TYPES.append(cls)
    return cls


class SemanticPiece(ABC):
    """
    This class represents a semantic piece in C/C++ code, and store some important data about it.
    Nodes and match results are not involved in this class.
    """

    _text: str

    @property
    def name(self) -> Optional[str]:
        return None

    @staticmethod
    @abstractmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None) -> Set[Node]:
        """
        extract the nodes that can build a semantic piece
        """
        raise NotImplementedError("extract in SemanticPiece is abstract")

    @staticmethod
    def extract_by_node_predicate(
        super_slice: Optional[CodeSlice],
        node_set: Optional[Set[Node]],
        predicate: Callable[[Node], bool],
    ) -> Set[Node]:
        if node_set is None:
            if super_slice is None:
                raise ValueError("super_slice and node_set can not be both None")
            node_set = set(parse_cpp(super_slice.text).root_node.children)

        child_set = set([child for child in node_set if predicate(child)])
        return child_set

    @classmethod
    def extract_by_node_type(
        cls, super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None, node_types: List[str] = []
    ) -> Set[Node]:
        if not node_types:
            logger.error("extract nodes by types, but the type list is empty!")
        return SemanticPiece.extract_by_node_predicate(super_slice, node_set, lambda child: child.type in node_types)

    @classmethod
    def create(cls: Type[Self], super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        if len(nodes) == 0:
            return None
        return cls(super_slice, nodes)

    def __init__(self, super_slice: Optional[CodeSlice], nodes: List[Node]):
        self.father: Optional[CodeSlice] = super_slice
        self._text: str = "\n".join([item.text.decode("utf-8") for item in nodes if item.text is not None])

    def match_similar_name(self, name_str: str, threshold: int = 80) -> bool:
        return match_similar_str(self.name, name_str, threshold)

    def match_exact_name(self, name_str: str) -> bool:
        return match_exact_str(self.name, name_str)

    @property
    def counter_kinds(self) -> List[str]:
        return []

    def match_exactly(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        if not self.counter_kinds:
            return None
        return match_exactly(self.name, rust_nodes, self.counter_kinds)

    def match_similarly(self, rust_nodes: Iterable[RustNode], threshold=90) -> Optional[MatchData]:
        if not self.counter_kinds:
            return None
        return match_similarly(self.name, rust_nodes, self.counter_kinds, threshold)

    def match_exactly_lower(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        if not self.counter_kinds:
            return None
        return match_exactly_lower(self.name, rust_nodes, self.counter_kinds)

    def match_similarly_lower(self, rust_nodes: Iterable[RustNode], threshold=90) -> Optional[MatchData]:
        if not self.counter_kinds:
            return None
        return match_similarly_lower(self.name, rust_nodes, self.counter_kinds, threshold)

    @property
    @abstractmethod
    def kind(self) -> str:
        raise NotImplementedError("kind in SemanticPiece is abstract")

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        return None

    @property
    def text(self) -> str:
        return self._text

    def matching_failure_description(self):
        return f"the source code contains\n{self.text}\n, but this part have no match in the translation result."


# single declarator
@conrete
class TypedefPiece(SemanticPiece):
    _name: str
    # body_kind: Optional[str]
    type_name: str
    body_piece: Optional["TypeDefinition"]

    @property
    def name(self) -> str:
        return self._name

    @classmethod
    def create(cls: Type[Self], super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        if not p:
            return None
        node = nodes[0]
        try:
            p._unsafe_set_name(node)
            p._unsafe_set_type_name(node)
            p._unsafe_set_body(node)
        except (NoSuchChild, NoText):
            return None
        return p

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None) -> Set[Node]:
        return SemanticPiece.extract_by_node_predicate(
            super_slice,
            node_set,
            lambda n: (n.type == "type_definition" and len(n.children_by_field_name("declarator")) == 1)
            or (n.type == "alias_declaration"),
        )
        # return SemanticPiece.extract_by_node_type(super_slice, node_set, ['type_definition', 'alias_declaration'])

    def _unsafe_set_type_name(self, node: Node):
        """
        Raises:
            NoSuchChild
            NoText
        """

        if node.type == "type_definition":
            t = unsafe_child_by_name(node, "type")
            possible_identifier = t.child_by_field_name("name")
            if possible_identifier:
                self.type_name = unsafe_text(possible_identifier)
            else:
                self.type_name = unsafe_text(t)
        elif node.type == "alias_declaration":
            tdescriptor = unsafe_child_by_name(node, "type")  # alias has a type descriptor with name type
            t = unsafe_child_by_name(tdescriptor, "type")  #  a type descriptor has a field type
            possible_identifier = t.child_by_field_name("name")
            if possible_identifier:
                self.type_name = unsafe_text(possible_identifier)
            else:
                self.type_name = unsafe_text(t)

    def _unsafe_set_name(self, node: Node):
        """
        Raises:
            NoSuchChild
            NoText
        """
        if node.type == "type_definition":
            dec = unsafe_child_by_name(node, "declarator")
            self._name = unsafe_declarator_name(dec)
        elif node.type == "alias_declaration":
            name_node = unsafe_child_by_name(node, "name")
            self._name = unsafe_text(name_node)

    def _unsafe_set_body(self, node: Node):
        """
        Raises:
            NoSuchChild
            NoText
        """
        body_kind = None
        body_node = None
        self.body_piece = None
        if node.type == "type_definition":
            type_node = unsafe_child_by_name(node, "type")
            ret = type_node.type.split("_")[0]
            if not type_node.child_by_field_name("body") is None:  # both struct and enum have body
                body_node = type_node
                body_kind = ret

        elif node.type == "alias_declaration":
            tdescriptor = unsafe_child_by_name(node, "type")  # alias has a type descriptor with name type
            t = unsafe_child_by_name(tdescriptor, "type")  #  a type descriptor has a field type
            ret = t.type.split("_")[0]
            if not t.child_by_field_name("body") is None:  # both struct and enum have body
                body_node = t
                body_kind = ret

        # if the typedef has a body, we are resonsible for construct a new piece
        if body_node:

            if body_kind == "enum":
                self.body_piece = EnumPiece.create(None, [body_node])
            elif body_kind == "union":
                self.body_piece = UnionPiece.create(None, [body_node])
            elif body_kind == "struct":
                self.body_piece = StructPiece.create(None, [body_node])
            elif body_kind == "class":
                self.body_piece = ClassPiece.create(None, [body_node])
            else:
                try:
                    body_text = unsafe_text(body_node)
                    logger.error(f"the typedef piece has a body:\n{body_text}\n that is of unkown type")
                except NoText:
                    pass

    def match_exact_type_name(self, type_name_str: str):
        return type_name_str == self.type_name

    def has_body(self) -> bool:
        return self.body_piece is not None

    # def body_kind(self) -> str:
    #     return self._body
    @property
    def kind(self) -> str:
        return "typedef"

    @property
    def counter_kinds(self) -> List[str]:
        return ["type_item"]

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        # first, try to match type_equal
        rust_nodes = list(rust_nodes)
        return (
            (self.body_piece and match_exactly(self.name, rust_nodes, self.body_piece.counter_kinds))
            or self.match_exactly(rust_nodes)
            or self.match_similarly(rust_nodes)
        )


# multiple typedef


@conrete
class MultipleTypedefPiece(SemanticPiece):
    body_cls: Optional[Type["TypeDefinition"]]
    raw_table: Dict[str, "TypeDefinition"]
    non_raws: List[str]

    @staticmethod
    def extract(super_slice, node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_predicate(
            super_slice, node_set, lambda n: n.type == "type_definition" and len(n.children_by_field_name("declarator")) > 1
        )

    @property
    def kind(self) -> str:
        return "multiple typedef"

    def _unsafe_set_body(self, node: Node) -> Node:
        """
        Raises:
            NoSuchChild
            NoText
        """
        t = unsafe_child_by_name(node, "type")
        ret = t.type.split("_")[0]

        # both struct and enum have body
        # self._body_node = t
        body_cls_str = ret
        self.body_cls = {"enum": EnumPiece, "union": UnionPiece, "struct": StructPiece, "class": ClassPiece}.get(body_cls_str)
        if self.body_cls is None:
            body_text = unsafe_child_text(t, "body")
            logger.error(f"the typedef piece has a body:\n{body_text}\n that is of unkown type")
        return t

    def _unsafe_set_declarators(self, declarators: List[Node], body_node: Node):
        """
        Raises:
            NoSuchChild
            NoText
        """
        self.raw_table = {}
        self.non_raws = []

        for d in declarators:
            d_name = unsafe_declarator_name(d)
            if not has_named_child(d, "declarator") and self.body_cls:
                p = self.body_cls.create(None, [body_node])
                if p is None:
                    continue
                self.raw_table[d_name] = p
            else:
                self.non_raws.append(d_name)

    def has_body(self) -> bool:
        return self.body_cls is not None

    @classmethod
    def create(cls: Type[Self], super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        if not p:
            return None
        node = nodes[0]

        try:
            body_node = p._unsafe_set_body(node)
            declarators = node.children_by_field_name("declarator")
            p._unsafe_set_declarators(declarators, body_node)
        except (NoSuchChild, NoText):
            return None
        return p

    # raw type for type_signature, typedefinition for body
    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        matches: List[MatchData] = []

        for d_name, db in self.raw_table.items():
            if (tmp := match_exactly(d_name, rust_nodes, db.counter_kinds)) is not None:
                matches.append(tmp)

        for d_name in self.non_raws:
            if (tmp := match_similarly(d_name, rust_nodes, "type_item", 90)) is not None:
                matches.append(tmp)

        return merge_matches(matches)


@conrete  # macro matches constants
class MacroPiece(SemanticPiece):
    macro_name: str
    has_value: bool

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["preproc_def"])

    @classmethod
    def create(cls: Type[Self], super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        # extract some useful message
        if not p:
            return None
        preproc_def = nodes[0]
        try:
            p.macro_name = unsafe_child_text(preproc_def, "name")
            p.has_value = has_named_child(preproc_def, "value")
        except (NoSuchChild, NoText):
            return None
        return p

    @property
    def counter_kinds(self) -> List[str]:
        return ["macro_definition"]

    @property
    def name(self) -> str:
        return self.macro_name

    def match_constant(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        return match_exactly_lower(self.macro_name, rust_nodes, "const_item")

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        rust_nodes = list(rust_nodes)
        return self.match_constant(rust_nodes) or self.match_exactly_lower(rust_nodes)

    @property
    def kind(self) -> str:
        return "macro"


@conrete
class MacroFunPiece(SemanticPiece):
    macro_name: str

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["preproc_function_def"])

    @property
    def kind(self) -> str:
        return "macro function"

    @property
    def counter_kinds(self) -> List[str]:
        return ["macro_definition"]

    @classmethod
    def create(cls, super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        if not p:
            return None
        try:
            p.macro_name = unsafe_child_text(nodes[0], "name")

        except (NoSuchChild, NoText):
            return None
        return p

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        return self.match_exactly_lower(rust_nodes)


# There are two possible declarations:
# Function Declaration
# int f();
# Variable Declaration
# int x;
# single;
@conrete
class DeclarationPiece(SemanticPiece):
    _name: str

    @property
    def name(self) -> str:
        return self._name

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_predicate(
            super_slice, node_set, lambda n: n.type == "declaration" and len(n.children_by_field_name("declarator")) == 1
        )

    def _unsafe_set_name_and_init(self, node: Node):
        """
        Raises:
            NoSuchChild
        """
        # at least, names and translated names are one-one corresponded
        # calculating types is a hard and error-prone job
        # so many declarators
        node = unsafe_child_by_name(node, "declarator")
        self._has_init = False
        if node.type == "init_declarator":
            self._has_init = True
        self._is_function = False
        if node.type == "function_declarator":
            self._is_function = True
        self._name = unsafe_declarator_name(node)

    @property
    def kind(self) -> str:
        return "declaration"

    @classmethod
    def create(cls: Type[Self], super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        if not p:
            return None
        p._unsafe_set_name_and_init(nodes[0])
        return p

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        if self._is_function:
            return match_exactly_lower(self.name, rust_nodes, "function_signature_item")
        else:
            return match_exactly_lower(self.name, rust_nodes, ["const_item", "static_item"])


@conrete
class MultipleDeclarationPiece(SemanticPiece):
    declares: List[str]
    has_inits: Dict[str, bool]
    is_funcs: Dict[str, bool]

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_predicate(
            super_slice, node_set, lambda n: n.type == "declaration" and len(n.children_by_field_name("declarator")) > 1
        )

    @property
    def kind(self) -> str:
        return "multiple declaration"

    def _unsafe_set_declarators(self, declarators: List[Node]):
        self.declares = []
        self.is_funcs = {}
        self.has_inits = {}
        for d in declarators:
            d_name = unsafe_declarator_name(d)
            d_has_init = d.type == "init_declarator"
            d_is_func = d.type == "function_declarator"
            self.declares.append(d_name)
            self.has_inits[d_name] = d_has_init
            self.is_funcs[d_name] = d_is_func

    def _match_declarator_case_insensitive(self, rust_nodes: Iterable[RustNode], d: str) -> Optional[MatchData]:
        if self.is_funcs[d]:
            return match_exactly_lower(d, rust_nodes, "function_signature_item")
        else:
            return match_exactly_lower(d, rust_nodes, ["const_item", "static_item"])

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        matches = [self._match_declarator_case_insensitive(rust_nodes, d) for d in self.declares]
        matches = filter(None, matches)
        return merge_matches(matches)

    @classmethod
    def create(cls, super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        if not p:
            return None
        node = nodes[0]
        try:
            declarators = node.children_by_field_name("declarator")
            p._unsafe_set_declarators(declarators)
        except (NoSuchChild, NoText):
            return None
        return p


@conrete
class FunctionPiece(SemanticPiece):
    _name: str

    @property
    def name(self) -> str:
        return self._name

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["function_definition"])

    def _safe_set_name(self, func_node: Node):
        """
        Raises:
            NoSuchChild
        """
        func_declarator = unsafe_child_by_name(func_node, "declarator")
        func_name = unsafe_declarator_name(func_declarator)
        self._name = func_name

    @classmethod
    def create(cls, super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        p = super().create(super_slice, nodes)
        if not p:
            return None
        try:
            p._safe_set_name(nodes[0])
        except NoSuchChild:
            return None
        return p

    # extern c
    # ordinary function

    @property
    def kind(self) -> str:
        return "function"

    @property
    def counter_kinds(self) -> List[str]:
        return ["function_item"]

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        rust_nodes = list(rust_nodes)
        return self.match_exactly(rust_nodes)
        # or self.match_similarly_lower(rust_nodes, 80)


# Toplevel ExpressionPiece, evil, but it exists
# incremental/decremental expression; update_expression
# function call(macro, specifically); call_expression
# assignment expression ; assignment_expression
# empty expression; empty exp
# @conrete
# class AssignmentPiece(SemanticPiece):
#     @staticmethod
#     def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
#         def predicate(node: Node) -> bool:
#             if node.type == "expression_statement":
#                 first_child = node.child(0)
#                 if first_child and first_child.type == "assignment_expression":
#                     return True
#             return False

#         return SemanticPiece.extract_by_node_predicate(
#             super_slice,
#             node_set,
#             predicate,
#         )

#     @property
#     def kind(self) -> str:
#         return "assignment"


# @conrete
# class UpdateExpPiece(SemanticPiece):
#     @staticmethod
#     def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
#         def predicate(node: Node):
#             if node.type == "expression_statement":
#                 first_child = node.child(0)
#                 if first_child and first_child.type == "update_expression":
#                     return True
#             return False

#         return SemanticPiece.extract_by_node_predicate(
#             super_slice,
#             node_set,
#             predicate,
#         )

#     @property
#     def kind(self) -> str:
#         return "update expression"


# @conrete
# class CallExpPiece(SemanticPiece):
#     @staticmethod
#     def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
#         def predicate(node: Node) -> bool:
#             if node.type == "expression_statement":
#                 first_child = node.child(0)
#                 if first_child and first_child.type == "call_expression":
#                     return True
#             return False

#         return SemanticPiece.extract_by_node_predicate(
#             super_slice,
#             node_set,
#             predicate,
#         )

#     @property
#     def kind(self) -> str:
#         return "call expression"


@conrete
class EmptyExpPiece(SemanticPiece):
    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        def predicate(node: Node) -> bool:
            if node.type == ";":
                return True
            if node.type == "expression_statement":
                first_child = node.child(0)
                if first_child and first_child.type == ";":
                    return True
            return False

        return SemanticPiece.extract_by_node_predicate(
            super_slice,
            node_set,
            predicate,
        )

    @property
    def kind(self) -> str:
        return "empty"


# @conrete
# class ExpressionPiece(SemanticPiece):
#     @staticmethod
#     def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
#         return SemanticPiece.extract_by_node_type(super_slice, node_set, ["expression_statement"])

#     @classmethod
#     def create(cls, super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
#         if nodes:
#             logger.error(f"A node of the base expression kind, which is unexpected to be initialized:\n {nodes[0].text}")
#         return super().create(super_slice, nodes)


class TypeDefinition(SemanticPiece):
    _name: Optional[str]
    full_name: Optional[str]

    @property
    def name(self) -> Optional[str]:
        return self._name

    @classmethod
    def create(cls: Type[Self], super_slice: Optional[CodeSlice], nodes: List[Node]) -> Optional[Self]:
        if cls == TypeDefinition:
            raise NotImplementedError("TypeDefinition is an abstract class")
        p = super().create(super_slice, nodes)
        if not p:
            return None
        p._set_name(nodes[0])
        return p

    def _set_name(self, node: Node):
        kind = self.kind
        if (name_node := node.child_by_field_name("name")) is not None and (text := name_node.text) is not None:
            name = text.decode("utf-8")
            self._name = name
            self.full_name = kind + " " + name
        else:
            self._name = None
            self.full_name = None


@conrete
class UnionPiece(TypeDefinition):

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["union_specifier"])

    @property
    def kind(self) -> str:
        return "union"

    @property
    def counter_kinds(self) -> List[str]:
        return ["union_item"]

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        return self.match_exactly(rust_nodes)


@conrete
class StructPiece(TypeDefinition):

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["struct_specifier"])

    @property
    def kind(self) -> str:
        return "struct"

    @property
    def counter_kinds(self) -> List[str]:
        return ["struct_item"]

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        rust_nodes = list(rust_nodes)
        return self.match_exactly(rust_nodes) or self.match_similarly(rust_nodes)


@conrete
class EnumPiece(TypeDefinition):
    enumerator_names: List[str]

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["enum_specifier"])

    @property
    def kind(self):
        return "enum"

    @property
    def counter_kinds(self) -> List[str]:
        return ["enum_item"]

    def _set_name(self, node: Node):
        super()._set_name(node)
        self.enumerator_names = []
        body_node = node.child_by_field_name("body")
        if body_node is None:
            return
        for item in body_node.children:
            if item.type != "enumerator":
                continue
            try:
                enumerator_name = unsafe_child_text(item, "name")
            except (NoSuchChild, NoText):
                continue
            self.enumerator_names.append(enumerator_name)

    # there are two possible ways to match an enum
    def match_arbitrary_named_definition(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        # so the source is nameless, we just need to match names of all items
        candidates = [item for item in rust_nodes if item.type == "enum_item"]
        lower_names = set([name.lower() for name in self.enumerator_names])
        for c in candidates:
            body_node = c.child_by_field_name("body")
            if body_node is None:
                continue
            # get names of all enumerators in the candidate
            enum_variant_lists = [item for item in body_node.children]
            enum_variant_lower_names = set()
            for item in enum_variant_lists:
                if item.type != "enum_variant":
                    continue
                try:
                    enumerator_name = unsafe_child_text(item, "name").lower()
                except (NoSuchChild, NoText):
                    continue
                enum_variant_lower_names.add(enumerator_name)
            if enum_variant_lower_names == lower_names:
                try:
                    return MatchData([c], [unsafe_child_text(c, "name")], unsafe_text(c))
                except (NoSuchChild, NoText):
                    continue
        return None

    def _match_enumerator_case_insensitive(self, rust_nodes: Iterable[RustNode], name: str, threshold=90) -> Optional[MatchData]:
        return match_similarly_lower(name, rust_nodes, ["const_item", "static_item"], threshold)

    def match_declarations(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        matches = [self._match_enumerator_case_insensitive(rust_nodes, n) for n in self.enumerator_names]
        matches = filter(None, matches)
        return merge_matches(matches)

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        rust_nodes = list(rust_nodes)
        return (
            self.match_exactly(rust_nodes)
            or self.match_arbitrary_named_definition(rust_nodes)
            or self.match_declarations(rust_nodes)
        )


@conrete
class ClassPiece(TypeDefinition):

    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["class_specifier"])

    @property
    def kind(self) -> str:
        return "class"

    @property
    def counter_kinds(self) -> List[str]:
        return ["struct_item"]

    def match(self, rust_nodes: Iterable[RustNode]) -> Optional[MatchData]:
        return self.match_exactly(rust_nodes)


@conrete
# extract comment
class CommentPiece(SemanticPiece):
    @staticmethod
    def extract(super_slice: Optional[CodeSlice], node_set: Optional[Set[Node]] = None):
        return SemanticPiece.extract_by_node_type(super_slice, node_set, ["comment"])

    @property
    def kind(self) -> str:
        return "comment"

    def match(self, rust_nodes):
        return MatchData([], [], "")


def extractPiecesFromSlice(s: CodeSlice) -> List[SemanticPiece]:
    # split slice to pieces
    pieces = []
    parse_treenode = parse_cpp(s.text).root_node

    node_set = set(parse_treenode.children)
    # make the type checker happy:
    k: Type[SemanticPiece]
    for k in CONCRETE_TYPES:
        selected_nodes = k.extract(s, node_set)
        for selected_node in selected_nodes:
            p = k.create(s, [selected_node])
            if p is not None:
                pieces.append(p)

        node_set -= selected_nodes

    if len(node_set) != 0:
        remains = [(n.text, n.type) for n in node_set]
        readpieces = [piece.__class__ for piece in pieces]
        errormsg = f"read file from the following slice failed:\n{s.text}\n The remains are\n{remains}\n There are {len(pieces)} read pieces:\n {readpieces} "
        logger.error(errormsg)

    # filter out empty expression
    pieces = list(filter(lambda p: not isinstance(p, EmptyExpPiece), pieces))
    return pieces
